#include <time.h>
#include <stdio.h>
#include "core/roa_acs_request.h"
#include "core/auth/credential.h"
#include "core/auth/i_signer.h"

namespace aliyun {

const char* kHeaderSeparator = "\n";
const char* kQuerySeparator = "&";

RoaAcsRequest::RoaAcsRequest(const string& product, const string& version, const string& action)
  : AcsRequest(product, version, action) {
  SetAcceptFormat("RAW");
}

RoaAcsRequest::~RoaAcsRequest() {
}

static string FormatToAccept(const string& accept_format){
  if(accept_format == "JSON") {
    return "application/json";
  } else if(accept_format == "XML") {
    return "application/xml";
  }
  return "application/octet-stream";
}

string RoaAcsRequest::ComposeUrl(ISigner *i_signer, const Credential* credential, const string& domain) {
  if(NULL == i_signer || NULL == credential) {
    return "";
  }
  time_t current_time = time(NULL);
  struct tm *gmt = gmtime(&current_time);
  char time_buffer[64];
  const char* week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  const char* month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  sprintf(time_buffer, "%s, %02d %s %04d %02d:%02d:%02d GMT",
    week[gmt->tm_wday], gmt->tm_mday, month[gmt->tm_mon],
    gmt->tm_year + 1900, gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
  headers_["Date"] = time_buffer;
  if(accept_format_.empty()) {
    accept_format_ = "RAW";
  }
  headers_["Accept"] = FormatToAccept(accept_format_);
  headers_["x-acs-signature-method"] = i_signer->GetSignatureMethod();
  headers_["x-acs-signature-version"] = i_signer->GetSignatureVersion();

  string sign_string = GetMethod() + kHeaderSeparator;
  std::map<string, string>::const_iterator iter;
  iter = headers_.find("Accept");
  if(iter != headers_.end()) {
    sign_string += iter->second;
  }
  sign_string += kHeaderSeparator;
  iter = headers_.find("Content-MD5");
  if(iter != headers_.end()) {
    sign_string += iter->second;
  }
  sign_string += kHeaderSeparator;
  iter = headers_.find("Content-Type");
  if(iter != headers_.end()) {
    sign_string += iter->second;
  }
  sign_string += kHeaderSeparator;
  iter = headers_.find("Date");
  if(iter != headers_.end()) {
    sign_string += iter->second;
  }
  sign_string += kHeaderSeparator;
  string uri = ReplaceOccupiedParameters();

  sign_string += BuildCanonicalHeaders();
  string query_string = BuildQueryString(uri);
  sign_string += query_string;
  headers_["Authorization"] = "acs " + credential->GetAccessKeyId() + ":"
    + i_signer->SignString(sign_string, credential->GetAccessSecret());
  return GetProtocol() + "://" + domain + query_string;
}

void RoaAcsRequest::SetVersion(const string& version) {
  version_ = version;
  headers_["x-acs-version"] = version;
}

const std::map<string, string>& RoaAcsRequest::GetPathParameters(void) const {
  return path_parameters_;
}
void RoaAcsRequest::PutPathParameter(const string& name, const string& value) {
  path_parameters_[name] = value;
}

const std::map<string, string>& RoaAcsRequest::GetDomainParameters(void) const {
  return domain_parameters_;
}
void RoaAcsRequest::PutDomainParameter(const string& name, const string& value) {
  domain_parameters_[name] = value;
}

const string& RoaAcsRequest::GetUriPattern(void) const {
  return uri_pattern_;
}
void RoaAcsRequest::SetUriPattern(const string& pattern) {
  uri_pattern_ = pattern;
}

string RoaAcsRequest::ReplaceOccupiedParameters(void) {
  string result = uri_pattern_;
  std::map<string, string>::const_iterator iter;
  for(iter = path_parameters_.begin(); iter != path_parameters_.end(); iter++) {
    string_replace(result, "["+iter->first+"]", iter->second);
  }
  return result;
}

string RoaAcsRequest::BuildCanonicalHeaders(void) {
  std::map<string, string> sort_map;
  std::map<string, string>::const_iterator iter;
  for(iter = headers_.begin(); iter != headers_.end(); iter++) {
    string key = iter->first;
    string_ToLower(key);
    if(key.compare(0, 6, "compare") == 0) {
      sort_map[key] = iter->second;
    }
  }
  string header_string;
  for(iter = sort_map.begin(); iter != sort_map.end(); iter++) {
    header_string += iter->first +":" + iter->second + kHeaderSeparator;
  }
  return header_string;
}

string RoaAcsRequest::BuildQueryString(const string& uri) {
  std::vector<string> uri_parts;
  string::size_type query_index = uri.find_first_of("?");
  if(query_index != string::npos) {
    uri_parts.push_back(uri.substr(0, query_index));
    uri_parts.push_back(uri.substr(query_index+1, uri.length()));
  } else {
    uri_parts.push_back(uri);
  }

  std::map<string, string> sort_map(query_parameters_);
  if(uri_parts.size() == 2) {
    sort_map.erase(uri_parts[1]);
  }
  string query_string = uri_parts[0] + "?";
  std::map<string, string>::const_iterator iter;
  for(iter = sort_map.begin(); iter != sort_map.end(); iter++) {
    query_string += iter->first;
    if(!iter->second.empty()) {
      query_string += "=" + iter->second;
    }
    query_string += kQuerySeparator;
  }
  if(sort_map.size() == 0) {
    query_string.erase(query_string.length()-1, 1);
  }
  return query_string;
}


} // namespace aliyun {

// end of file
